-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add experimental support for string registers #603
base: master
Are you sure you want to change the base?
Conversation
Converted to draft as I got some bugs when I want to use parameters in strings 😅 |
FYI |
Hah, damnit. I can't very well take |
Looks like ampesand ( |
I've been pondering over this while going back and improving the whole parameters and expression implementation... |
Something like this to set string registers:
|
While it would work, I am not sure I like it. Having comments that are altering semi-persistent stuff feels a bit "wrong", even though I understand the sentiment 😅 And although the current use for string-registers is strictly in comments, I would not be surprised if someone would use them to create engraving plugins etc, and then it would start to look strange to set/modify them only within comments. As for solving the normalization, I think using quotes will be an elegant solution, as it would then also work if someone at some point adds some other feature that needs to use strings for anything else 🤔 And parameter_substitution can be done in the normalization, which would make that feature available for any feature that needs strings in the future. But in any case, I would probably keep the syntax for setting and reading similar;
or
not
🤔 |
This belongs in a discussion, but one thing I have thought a little about somehow making a "handler-system" where you would have the default handlers in grbl-core for everything that is core-functionality (supported G-codes and M-codes etc), and plugins could register either customizations of existing handlers or add completely new handlers. That way this could be a plugin that registered a "block-handler" and a "substitution-handler" for Though I am far away from understanding enough of the code to be able to implement something like this yet other than that it would require a complete rewrite of most of gcode-handling, and is borderline impossible without a test-framework 😅 Edit; Now that I think of it, it might not be too hard to implement such a system for non-letter characters 🤔 |
Strings outside comments in G-code is even stranger, no dialects I know of supports them... The Fanuc SR registers are for robots which is a very specialiced segment? There is also prior use of sending data outside the scope of normal G-code via comments, IIRC LinuxCNC passes some info to its plasma THC implementation that way. And if IIRC that is the reason I added the I agree that gcode.c would benefit from a rewrite, it is an insanely long function. But it is not something that is achieved in a few afternoons, and it works, so better to leave it for later when there is nothing else to improve on? I have been tinkering a bit more with your string support and want to use it to implement named subroutine calls: A tip: the latest versions supports indexed parameter access with ##n and #[#n] syntax (n can be an expression]. |
Been busy "playing" carpenter this weekend, so have to try to rebase and add the discussed changes some time during the coming week 👍 |
… does not need to be in []
… previous returns
as I am unsure if the ngc_-prefix should be there on this
…creating texts with values from parameters etc
…TS, DTR) changes - initially for USB streams.
@terjeio Trying to move setting string registers to a onGcodeComment-function in string-registers.c causes a circular reference because getting the id of the register to set uses ngc_eval_expression, but ngc_substitute_parameters does string-register substitution and so needs to know about string registers as well 🤔 😅 Maybe substitution of parameters and string registers also should be done using a core handler, like onGcodeComment? 🤔 |
…sters to onGcodeComment
I have now done some initial testing, and it does seem like this is working. But I have not yet figured out a good way to check for memory leaks. How do you do this, @terjeio ? 🤔 Would be nice if it was possible to get some type of unit testing on the repo as well, so it would be possible to test without deploying to a controller. Not sure how to do that though 😞 |
True - but you do not need to include the .h file, just declare the needed function, string_register_get(), as extern in ngc_expr.c. The way you have done it now is, IMO, overly complicated. I am not keen on the new custom status code returns you have added, the old signatures were just fine - just do not output any messages in the string registers code, leave it to the caller if it is needed. BTW string substitution in comments should never cause an error return to the top level - any fault should be handled gracefully. If you insists on having status code returns add them to error.h if there is no suitable codes available there already.
By testing edge cases, and by knowing the behaviour of library calls. Still I miss some sometimes...
You can use the Simulator driver. I prefer to run tests on a controller, and I make heavy use of the debugger while doing so. I want to add some functions your string register code in order to support named o subroutines/calls via some trickery (by using 32 bit ids). To do this I want you to use |
Hm, why, though? if realloc returns null, my intention was that the string register just remains unchanged? 🤔 |
Isn't that bad behaviour? I would remove it from the list and return false indicating that setting the register failed. |
Not really. Take my example where I append more content on a string register; if appending the extra info fails, I would much prefer to have the base data so that I can try again, rather than losing everything 😅 Regardless; it is not a memory leak when it can still be used/reassigned later, right? 🤔 And if it happens while setting a new SR, it will not have allocated any memory to free, so then it also does not matter either |
Ah, I forgot that you could do that in C/C++ 😅 While my solution is more complex, it does make string substitution more atomic, where parameter-substitution is completely separated from string register substitution, and others could make plugins that expand it (string substitution) further if they want/need to |
I would rather not have som random data coming back later if I decide to reuse a register... |
With all due respect, that is a completely unrelated issue (persisted vs working memory). It is not really that surprising that a change made during runtime is not persisted after a soft reset if there is some issue that causes persistence to fail. Also, it would not be random data, it would be whatever it was before the failed attempt. The error returned should be the hint that something went wrong, not destroying data. Also, I have not checked, but I am pretty sure that if you change a setting using $x= or a parameter using #x= and it fails, it does not erase the previous value, as that would be very unexpected and destructive behaviour. Any operation should either have the desired effect, or no effect. This is just best practice in software development. |
Hm, I agree that string substitution should handle errors more gracefully and not end up returning a error code. And maybe these messages that I posted were mostly not that necessary outside me trying to debug the feature itself 🤔 Though I guess returning an error code for "input too long/large" would be good, so I can add that if none exists already, maybe? 🤔 |
I am not sure I understand why this would require using |
That depends on how you view it. If I run a gcode program that sets a string register and later I run a new one that sets the same register and fails then I would consider it kind of random, or at least totally unrelated, to what I want it to be.
There are two variants of o call and o sub, numbered where the subroutine is part of the running gcode program and named which is another gcode program located in a filing system where name = the filename of the file. I have already added support for numbered subroutines, to add support for named ones I want to use string registers for temprary storage of the name and use the (automatically assigned) string register id to pass information to ngc_expr.c. The reason for this is primarily that gcode is not designed to handle strings. And since ngc_param_id_t is 16 bit I can differentiate between a "normal" string register and one used by ngc_expr.c for subroutines by assigning ids above 2^16. Some inactive code here and here if you want to take a look. |
I would argue that in many scenarios, you would want the value to remain unchanged. IE; you have a string register with the current serial number as a string. every time you run your engraving code, you increment the serial number and regenerate the text. If unable to do so, I would prefer to fetch the register and see what the last successful value was, rather than it being deleted. In any case; Your gcode program will stop and alert you if there is an error writing to a string register, but it is a lot more hassle to make it repair destroyed data. You would have to make a copy of the register you are about to change first (and a parameter to keep track of the id of the register), and then you have to have a separate program to restore the register that you could run after the program failed 😅 🙈
|
@terjeio Reading through Calling Files, I get the feeling it would be cleaner to add I think also I would pass a pointer to That would mean this code;
could be simplified to this:
And then the handling in
to something similar to:
And I guess you would also need to free |
Won't work, and having an event is not needed since I do not see any reason why plugin code would (or should) subscribe to it.
Of which none is allowed other than N (line number), which is of no use. The rest of the block is to be parsed for expressions to pass on in parameters |
Ok, I see there is much here I still do not understand. I thought this was only going to be used for a single block ( But I still do not fully understand why you want string-registers to use |
I have already coded it, and the other functions I need as well. Works as intended.
IMO they are kind of, of string type. But live in a different space. You may keep string_register_t if more logical for you, but keep it as 16 bit, perhaps by typedef'ing it from ngc_param_id_t? I'll add a different type for the 32 bit variant if so.
I keep an unsigned 32 bit counter for the id's that starts from
|
…turns status-codes itself
@@ -599,14 +607,16 @@ char *gc_normalize_block (char *block, status_code_t *status, char **message) | |||
if(message && *message == NULL) { | |||
#if NGC_EXPRESSIONS_ENABLE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@terjeio Should handling of (DEBUG,
and (PRINT,
be moved to ngc_flowctrl
's onGcodeComment
? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Since substitution is done in ngc_expr.c it belongs there, and also because the grbl.on_gcode_comment()
event is not able to return a string (and should not be changed to accomodate that).
Your grbl.on_string_substitution()
event could be set up at startup to point to a new function in ngc_expr.c, making it overridable. And then your string register handling could (or rather should?) be moved to a plugin, in this repo? grbl.on_string_substitution
has to be changed to a more descriptive name if doing so...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aha, maybe I should make a separate PR with just the string substitution-stuff, and move string registers to a plugin-repo? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The previous comment was somehow posted before I finished, trying again:
Aha, maybe I should make a separate PR with just the string substitution-stuff, and move string registers to a plugin-repo?
I'll update the core, you add a plugin for string registers and string substitution that includes string registers. This since I'll make comment handling completely overridable even for (MSG, ...), in gcode.c:
case ')':
if(comment && !gc_state.skip_blocks) {
*s1 = '\0';
if(!hal.driver_cap.no_gcode_message_handling) {
if(message && *message == NULL) {
if(grbl.on_process_gcode_comment)
*message = grbl.on_process_gcode_comment(comment);
if(*message == NULL) {
size_t len = s1 - comment - 3;
if(!strncasecmp(comment, "MSG,", 4) && (*message = malloc(len))) {
comment += 4;
while(*comment == ' ') {
comment++;
len--;
}
memcpy(*message, comment, len);
}
}
}
}
if(*comment && *message == NULL && grbl.on_gcode_comment)
*status = grbl.on_gcode_comment(comment);
}
comment = NULL;
break;
grbl.on_process_gcode_comment
will be defaulted to handle (PRINT, ..) and (DEBUG, ...) when expressions is enabled.
You can choose to do string replacements only and use the default handler for numeric ones, or replace the handler completely. Note that the grbl.on_process_gcode_comment
signature does not allow for a status code return - this is deliberate since message comments should never fail with an error.
You may choose where to publish a string register plugin, you can add it to your own repo or make a PR for the misc plugins repo. The Web Builder can build with repos located outside grblHAL, but local builds cannot without manually adding your code to the source tree - at least not without adding it as a submodule which I am reluctant to do.
FYI I have already added string parameter support to ngc_param.c (not yet comitted) in order to handle named o calls, you may build string registers on top of this or add your version to your plugin. It is different from your string register code in that it does not return status codes and it will remove a string parameter on failure to allocate for it. Also, it will not reallocate on changes if the new length is less than the previous, this as an attempt to reduce heap fragmentation.
Taking inspiration from named ngc-parameters, I have added experimental support for string registers. These can currently only be used to insert the text from the registers into messages to the sender
(MSG,
,(DEBUG,
and(PRINT,
, but that is also the use-case I view as most useful for now.Example use is for storing tool descriptions;
upon tool change, you can give the user a description of the tool to load:
Or a little more complicated:
P210 (store tool information):
P200.macro (find rack slot for tool):
Then in my programs, I list the tools at the start:
which then results in messages for the operator, so that they understand which tools to load where:
Put tool T1: D4 L10 flat end mill in slot 1 from the back
Put tool T4: D6 CR0.5 bull nose end mill in slot 2 from the back
Updated to reflect how setting string registers is done using comments